/*
 *  serveur.c
 *  pire2pireV1
 *
 *  Created by jihed kamkoum on 04/05/10.
 *  Copyright 2010 ENSEIRB. All rights reserved.
 *
 */

#include "serveur.h"
void setnonblocking(int sock){
	int opts;
	opts = fcntl(sock,F_GETFL);
	if (opts < 0) {
		perror("fcntl(F_GETFL)");
		exit(EXIT_FAILURE);
	}
	opts = (opts | O_NONBLOCK);
	if (fcntl(sock,F_SETFL,opts) < 0) {
		perror("fcntl(F_SETFL)");
		exit(EXIT_FAILURE);
	}
}
void build_select_list(int sock,fd_set* socks,int * connectlist,int* highsock,int fdpipe ) {
	int listnum;	   
	FD_ZERO(socks);
	FD_SET(sock,socks);
	FD_SET(fdpipe,socks);
	if (fdpipe > *highsock)
		*highsock = fdpipe;
	for (listnum = 0; listnum < FDMAX; listnum++) {
		if (connectlist[listnum] != 0) {
			FD_SET(connectlist[listnum],socks);
			if (connectlist[listnum] > *highsock)
				*highsock = connectlist[listnum];
		}
	}
}
void timeout_handler(int *connectlist,Liste * listeClients, float time_out){
	int listnum;
	float duration;
	for (listnum=0; listnum<FDMAX;listnum++) {
		if((findBySocket(connectlist[listnum], listeClients))!=NULL){
			clock_t last_event = (findBySocket(connectlist[listnum], listeClients))->last_event;
			clock_t now = clock();
			duration = 10000*((float)now - last_event)/CLOCKS_PER_SEC;
			if (duration>time_out) {
				supprimer(listeClients, *(findBySocket(connectlist[listnum], listeClients)) );
				sock_puts(connectlist[listnum], "connection cancled \n");
				connectlist[listnum] = 0;
			}
		}
	}
	
	
}
void sock_puts(int sock,const char* message){
	char buffer[BUFSIZ];
	memset((char *) &buffer, 0, sizeof(buffer));
	strcpy(buffer,message);
	buffer[strlen(message)+1]='\0';
	if ((send(sock, buffer, BUFSIZ, 0)==-1)) {
		perror("send");
	}
}
void handle_new_connection(int sock,int *connectlist,Liste * listeClients,pthread_mutex_t *clientTableAccess) {
	int listnum;
	int connection;
	SOCKADDR_IN csin;
	socklen_t crecsize = sizeof(csin);
	timeout_handler(connectlist,listeClients,300.0);// 300 : temps max sans activitee
	connection = accept(sock, (SOCKADDR*)&csin, &crecsize);
	if (connection < 0) {
		perror("accept");
		exit(EXIT_FAILURE);
	}
	setnonblocking(connection);
	for (listnum = 0; (listnum < FDMAX) && (connection != -1); listnum ++)
		if (connectlist[listnum] == 0) {
			printf("\nConnection accepted:   FD=%d; Slot=%d\n",connection,listnum);
			connectlist[listnum] = connection;
			variable *client;
			client = malloc(sizeof(variable));
			strcpy(client->ip,(const char *)inet_ntoa(csin.sin_addr));
			client->port = htons(csin.sin_port);
			client->last_event = clock();
			client->socket = connection;
			//debut section critique
			pthread_mutex_lock(clientTableAccess);
			inserer(listeClients, client);
			pthread_mutex_unlock(clientTableAccess);
			//fin section critique
			sock_puts(connection, "Connection accepted\n");
			connection = -1;
		}
	if (connection != -1) {
		/* No room left in the queue! */
		printf("\nNo room left for new client.\n");
		//envoyer un message au client
		sock_puts(connection, "Sorry, this server is too busy. Try again later!\r\n");
		close(connection);
	}
	
}
void create_serveur_sock(SOCKET * sock){
	/* Création d'une socket */
	
	*sock = socket(AF_INET, SOCK_STREAM, 0);
	if(*sock == INVALID_SOCKET){
		perror("socket");
		exit(EXIT_FAILURE);
	}
	/* Si la socket est valide */
	printf("La socket %d est maintenant ouverte en mode TCP/IP\n", *sock);
}
void bind_serveur(SOCKET * sock,char * ip,int port,char * path,int semid,Liste * listeClients,filelist_t * listeFichiers,int fdpipe,pthread_mutex_t * clientsTableAccess,pthread_mutex_t * fileTableAccess,pthread_mutex_t * trafficTableAccess){
	sleep(rand()%3);
	//¡Ay, caramba! 
	loop1 :
	if(*sock!=-1){
		printf("Fermeture de la socket serveur\n");
		if((close(*sock)==-1))
			perror("close");
		printf("Fermeture du serveur terminée\n");
		//liberation des tables
		supprimer_liste(listeClients);
	}	
	create_serveur_sock(sock);
	int reuse_addr = 1;
	struct timeval timeout; 
	int readsocks;
	char events[80];
	//memset((char *) &events, "\0", sizeof(events));
	setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,sizeof(reuse_addr));
	setnonblocking(*sock);
	
    SOCKADDR_IN sin;
	bzero((char*)&sin, sizeof(SOCKADDR_IN));
    socklen_t recsize = sizeof(sin);	
    int sock_err;
	//debut section critique
	pthread_mutex_lock(clientsTableAccess);
	listeClients = malloc(sizeof(Liste));
	init_liste(listeClients);
	pthread_mutex_unlock(clientsTableAccess);
	//fin section critique
	
	/* Configuration */
	sin.sin_addr.s_addr = inet_addr(ip); 
	printf("%s\n",inet_ntoa(sin.sin_addr));
	sin.sin_family = AF_INET;                 /* Protocole familial (IP) */
	sin.sin_port = htons(port);               /* Listage du port */
	if((sock_err = bind(*sock, (SOCKADDR*)&sin, recsize))== SOCKET_ERROR){
		perror("Bind");
		close(*sock);
		exit(EXIT_FAILURE);
	}
	/* Démarrage du listage (mode server) */
	if((sock_err = listen(*sock, FDMAX))== SOCKET_ERROR){
		perror("listen");
	}
	printf("Listage du port %d...\n", port);
	int highsock = *sock;
	fd_set socks;
	int connectlist[FDMAX];
	// Initialisation du tableau de socket
	memset((char *) &connectlist, 0, sizeof(connectlist));
	//int i;
	/*initialisation de la semaphore */
	if((semctl(semid, 0, SETVAL,NBTHREAD)==-1)){
		perror("semctl");
	}
	printf("Patientez pendant que le client se connecte sur le port %d...\n", port);
	
	while(1)
	{
		/* Attente pendant laquelle le client se connecte */
		setnonblocking(fdpipe);
		build_select_list(*sock,&socks,connectlist,&highsock,fdpipe);
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		readsocks = select(highsock+1, &socks, (fd_set *) 0,(fd_set *) 0, &timeout);
		if (readsocks < 0) {
			perror("select");
			exit(EXIT_FAILURE);
		}
		if (readsocks == 0) {
			//printf(".");
			fflush(stdout);
			if((sched_yield()==-1)){
				perror("yield");
				sleep(5);
			}
			
		} else{
			if (FD_ISSET(fdpipe,&socks)) {
				
				if((read(fdpipe, events, sizeof(events))==-1)){
					perror("read");
					exit(EXIT_FAILURE);
				}
				char* cmd=malloc(10*sizeof(char));
				char* param=malloc(40*sizeof(char));
				if(!decomposer_buff(events,cmd,param))//S'il n'y a pas de parametre
					param=NULL;
				
				int setret = cmd_set(param,&ip,&port,&path);
				if(param!=NULL && setret==0){
					goto loop1;
				}
				free(cmd);
				free(param);
				
			}
			read_socks(*sock,&socks,connectlist,semid,listeClients,listeFichiers,clientsTableAccess,fileTableAccess);
		}
		
		
	}
	
}

void read_socks(int sock,fd_set *socks,int * connectlist,int semid,Liste * listeClients,filelist_t * listeFichiers,pthread_mutex_t *clientTableAccess,pthread_mutex_t * fileTableAccess) {
	int listnum;
	if (FD_ISSET(sock,socks))
		handle_new_connection(sock,connectlist,listeClients,clientTableAccess);	
	for (listnum = 0; listnum < FDMAX; listnum++) {
		if (FD_ISSET(connectlist[listnum],socks)){
			//debut section critique
			pthread_mutex_lock(clientTableAccess);
			if((findBySocket(connectlist[listnum], listeClients))!=NULL){
				(findBySocket(connectlist[listnum], listeClients))->last_event = clock();
			}
			pthread_mutex_unlock(clientTableAccess);
			//fin section critique
			deal_with_data(listnum,connectlist,semid,listeClients,listeFichiers,clientTableAccess,fileTableAccess);
			
		}
	}
}
void deal_with_data(int listnum,int * connectlist,int semid,Liste * listeClients,filelist_t * listeFichiers,pthread_mutex_t *clientTableAccess,pthread_mutex_t * fileTableAccess){
	
	char buffer[BUFSIZ];
	memset((char *) &buffer, 0, sizeof(buffer));
	int ret;
	struct sembuf op;
	op.sem_num = 0; //Test si le thread est dispo
	op.sem_op = -1; //P()
	op.sem_flg = 0; //attente de l'operation
	if((ret=recv(connectlist[listnum], buffer, sizeof(buffer), 0))==-1)
		perror("receive");
	else if(ret !=0){
		if((semop(semid, &op, 1)==0)){
			/*thread disponnible*/
			pthread_t threadClient;
			/*creation du thread*/
			//les arguments de la thread
			cmdrecv * recived = malloc(sizeof(cmdrecv));
			recived->sock = connectlist[listnum];
			recived->semid = semid;
			recived->cmd = buffer;
			recived->listeClients = listeClients;
			recived->clientTableAccess = clientTableAccess;
			recived->listeFichiers = listeFichiers;
			recived->fileTableAccess = fileTableAccess;
			//recived->
			printf("Chaine recue : %s\n",buffer);
			if((pthread_create(&threadClient, NULL,(void *)f_thread_serveur,recived))==-1)
				printf("probleme pthread_create \n");
			else {
				changer_ordo_priorite(threadClient, SCHED_RR, 5);
			}
		}
		else{
			perror("semop P()");
		}		
	}
		
}

//Routine de traitement de la requete client
void * f_thread_serveur(cmdrecv * recived){
	printf("debut thread %d\n",pthread_self());
	sleep(rand()%3);
	int semid = recived->semid;
	char message[NAMESIZE];
	sscanf(recived->cmd, "%s", message);	
	/*debut traitement*/
	if (strcmp(message, "get") == 0) {
		int key,begin,end;
		char name[NAMESIZE];
		sscanf(recived->cmd,"%s %d %d %d",name,&key,&begin,&end);
		int i ;
		for (i=0; i<end; i++) {
			printf("Iteration %d thread %d  \n",i,pthread_self());
			sched_yield();
			sleep(5);
		}
	}
	if (strcmp(message, "neighbourhood") == 0) {
		cmd_neighbourhoud_handler(recived);

		}
	
	/*Liberation d'un thread client*/
	struct sembuf op;
	op.sem_num = 0; //Test si le thread est dispo
	op.sem_op = 1; //V()
	op.sem_flg = 0; //aucune importance
	if((semop(semid, &op, 1)==-1)){
		perror("semop V()");
	}
	printf("fin thread %d\n",pthread_self());
	pthread_exit(NULL);
}
void cmd_get_handler(cmdrecv * recived){
	int key,begin,end;
	char name[NAMESIZE];
	sscanf(recived->cmd,"%s %d %d %d",name,&key,&begin,&end);
	

}
void cmd_neighbourhoud_handler(cmdrecv* recived){
	if ((recived->listeClients)->taille!=0){
		char taille[NAMESIZE];
		memset((char *) &taille, '\0', sizeof(taille));
		sprintf(taille,"%d",(recived->listeClients)->taille);
		if ((send(recived->sock, taille, NAMESIZE, 0)==-1)) {
			perror("send");
		}
		printf("%d\n",recived->sock);
		EltListe *elt;
		char buffer2[BUFSIZ];
		memset((char *) &buffer2, '\0', sizeof(buffer2));
		//debut section critique
		pthread_mutex_lock(recived->clientTableAccess);
		elt=malloc(sizeof(EltListe));
		elt=(recived->listeClients)->fin;
		while(elt!=NULL){
			if((elt->donnees).socket != recived->sock){
				sprintf(buffer2,"neighbour %s : %d",(elt->donnees).ip,(elt->donnees).port);
				sock_puts(recived->sock,buffer2);
				sched_yield();
			}
			elt=elt->precedent;
		}
		pthread_mutex_unlock(recived->clientTableAccess);
		//fin section critique
		
	}
}